package br.com.gr.jcash.bo;

import br.com.gr.jcash.dao.ContaDAO;
import br.com.gr.jcash.dmp.Categoria;
import br.com.gr.jcash.dmp.Conta;
import br.com.gr.jcash.dmp.Movimento;
import br.com.gr.jcash.dmp.TipoCategoria;
import br.com.gr.jcash.dmp.Usuario;
import br.com.gr.jcash.dto.ContaDTO;
import br.com.gr.jcash.exception.JCashException;
import br.com.gr.jcash.iface.IContaBO;
import br.com.gr.jcash.iface.IMovimentoBO;
import br.com.gr.jcash.iface.IUsuarioBO;
import br.com.gr.jcash.utils.Filtro;
import br.com.gr.jcash.utils.RelatorioHelper;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.annotation.Resource;
import javax.ejb.EJB;
import javax.ejb.EJBContext;
import javax.ejb.Local;
import javax.ejb.Stateless;

@Stateless(mappedName = "ejb/ContaBO")
@Local(IContaBO.class)
public class ContaBO implements IContaBO {

    @EJB
    private ContaDAO dao;
    @EJB
    private IUsuarioBO usuarioBO;
    @EJB
    private IMovimentoBO movimentoBO;
    @Resource
    private EJBContext context;

    public ContaBO() {
    }

    @Override
    public void criar(Conta entidade) throws JCashException {
        entidade.setUsuario(getUsuario());
        validarConta(entidade);
        dao.incluir(entidade);
    }

    @Override
    public Conta editar(Conta entidade) throws JCashException {
        validarConta(entidade);
        return dao.atualizar(entidade);
    }

    @Override
    public void remover(Conta entidade) throws JCashException {
        if (movimentoBO.existeMovimentoPorConta(entidade)) {
            throw new JCashException("Não é possível excluir, existe movimento para essa categoria");
        } else {
            dao.remover(entidade);
        }
    }

    @Override
    public Conta buscarPorId(Long id) throws JCashException {
        return dao.buscarPorId(id);
    }

    @Override
    public List<Conta> buscarTodos() throws JCashException {
        return dao.buscarTodos(getUsuario());
    }

    @Override
    public Long count() throws JCashException {
        return dao.count(getUsuario());
    }

    @Override
    public List<Conta> buscarPorFaixa(int[] faixa) throws JCashException {
        return dao.buscarPorFaixa(getUsuario(), faixa);
    }

    private Usuario getUsuario() throws JCashException {
        return usuarioBO.buscarPorLogin(context.getCallerPrincipal().getName());
    }

    @Override
    public List<Conta> buscarContasOrigem() throws JCashException {
        return dao.buscarPorTipoCategoria(getUsuario(), TipoCategoria.C, TipoCategoria.R);
    }

    @Override
    public List<Conta> buscarContasDestino() throws JCashException {
        return dao.buscarPorTipoCategoria(getUsuario(), TipoCategoria.C, TipoCategoria.D);
    }

    @Override
    public List<Conta> buscarContaPorCategoria(Categoria categoria) throws JCashException {
        return dao.buscarTodos("Conta.buscarPorCategoria", categoria, getUsuario());
    }

    @Override
    public List<Conta> buscarContaPorTipoCategoria(TipoCategoria... tipo) throws JCashException {
        return dao.buscarPorTipoCategoria(getUsuario(), tipo);
    }

    private void validarConta(Conta conta) throws JCashException {
        if (conta.getCategoria().getCatTipo() == TipoCategoria.C) {
            if (null == conta.getCtaDataInicial() || null == conta.getCtaSaldoInicial()) {
                throw new JCashException("Informar a data inicial e saldo inicial para contas de capital!");
            }
        } else if (null != conta.getCtaDataInicial() || null != conta.getCtaSaldoInicial()) {
            throw new JCashException("Não informar a data inicial e saldo inicial para contas de receita ou despesa!");
        }
    }
    
    private void validarAlteracao(Conta conta) throws JCashException {
        if (conta.getCategoria().getCatTipo() == TipoCategoria.C) {
            if (movimentoBO.existeMovimentoPorContaAnteriorData(conta)) {
                throw new JCashException("Não é possível alterar, existe lançamento anterior a data inicial!");
            }
        }
    }

    @Override
    public boolean existeContaPorCategoria(Categoria categoria) throws JCashException {
        if (dao.countContaPorCategoria(getUsuario(), categoria) >= 1) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public RelatorioHelper buscarExtrato(Filtro filtro) throws JCashException {
        Date inicioConta = filtro.getConta().getCtaDataInicial();
        BigDecimal entradasAcumuladas = new BigDecimal("0");
        BigDecimal saidasAcumuladas = new BigDecimal("0");
        
        entradasAcumuladas = entradasAcumuladas.add(dao.buscarSomaEntradasEntreDatas(getUsuario(), filtro.getConta(), inicioConta, filtro.getDataInicial()));
        saidasAcumuladas = saidasAcumuladas.add(dao.buscarSomaSaidasEntreDatas(getUsuario(), filtro.getConta(), inicioConta, filtro.getDataInicial()));
        BigDecimal saldo = filtro.getConta().getCtaSaldoInicial();
        RelatorioHelper helper = new RelatorioHelper();
        
        saldo = saldo.add(entradasAcumuladas);
        saldo = saldo.subtract(saidasAcumuladas);
        
        helper.setSaldoAnterior(saldo);
        
        List<ContaDTO> listaContaDTO = new ArrayList<>();
        List<Movimento> lista = movimentoBO.buscarPorContaEntreDatas(filtro.getConta(), filtro.getDataInicial(), filtro.getDataFinal());
        for (Movimento movimento : lista) {
            ContaDTO conta = new ContaDTO();
            conta.setData(movimento.getMvtData());
            conta.setId(movimento.getIdMvt());
            conta.setHistorico(movimento.getMvtHistorico());
            
            if (movimento.getCtaDestino().getIdCta() == filtro.getConta().getIdCta()) {

                conta.setEntrada(movimento.getMvtValor());
                saldo = saldo.add(movimento.getMvtValor());
            }
            if (movimento.getCtaOrigem().getIdCta() == filtro.getConta().getIdCta()) {
                conta.setSaida(movimento.getMvtValor());
                saldo = saldo.subtract(movimento.getMvtValor());
            }

            conta.setSaldo(saldo);
            
            listaContaDTO.add(conta);
        }
        
        helper.setLista(listaContaDTO);
        return helper;
    }
}
